#ifndef _DEPOOLHASHARRAY_H
#define _DEPOOLHASHARRAY_H
/*-------------------------------------------------------------------------
 * drawElements Memory Pool Library
 * --------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Memory pool hash-array class.
 *//*--------------------------------------------------------------------*/

#include "deDefs.h"
#include "dePoolHash.h"
#include "dePoolArray.h"

DE_BEGIN_EXTERN_C

void	dePoolHashArray_selfTest		(void);

DE_END_EXTERN_C

/*--------------------------------------------------------------------*//*!
 * \brief Declare a template pool hash-array (array with hash) class interface.
 * \param TYPENAME			Type name of the declared hash-array.
 * \param KEYTYPE			Type of the key.
 * \param VALUETYPE			Type of the value.
 * \param KEYARRAYTYPE		Type of the key array.
 * \param VALUEARRAYTYPE	Type of the value array.
 *
 * \todo [petri] Description.
 *
 * The functions for operating the hash are:
 * \todo [petri] Figure out how to comment these in Doxygen-style.
 *
 * \todo [pyry] HashArray_find() will break if dePoolArray implementation changes.
 *
 * \code
 * HashArray*  HashArray_create            (deMemPool* pool);
 * int         HashArray_getNumElements    (const HashArray* hashArray);
 * Value*      HashArray_find              (Hash* hashArray, Key key);
 * deBool      HashArray_insert            (Hash* hashArray, Key key, Value value);
 * deBool      HashArray_copyToArray       (Hash* hashArray, KeyArray* keys, ValueArray* values);
 * \endcode
*//*--------------------------------------------------------------------*/
#define DE_DECLARE_POOL_HASH_ARRAY(TYPENAME, KEYTYPE, VALUETYPE, KEYARRAYTYPE, VALUEARRAYTYPE)		\
																									\
DE_DECLARE_POOL_ARRAY(TYPENAME##Array, VALUETYPE);													\
DE_DECLARE_POOL_HASH(TYPENAME##Hash, KEYTYPE, int);													\
																									\
typedef struct TYPENAME_s																			\
{																									\
	TYPENAME##Hash*		hash;																		\
	TYPENAME##Array*	array;																		\
} TYPENAME; /* NOLINT(TYPENAME) */																	\
																									\
TYPENAME*		TYPENAME##_create		(deMemPool* pool);											\
deBool			TYPENAME##_insert		(DE_PTR_TYPE(TYPENAME) hashArray, KEYTYPE key, VALUETYPE value);	\
deBool			TYPENAME##_copyToArray	(const TYPENAME* hashArray, DE_PTR_TYPE(KEYARRAYTYPE) keys, DE_PTR_TYPE(VALUEARRAYTYPE) values);	\
																									\
DE_INLINE int			TYPENAME##_getNumElements	(const TYPENAME* hashArray)					DE_UNUSED_FUNCTION;	\
DE_INLINE VALUETYPE*	TYPENAME##_find				(const TYPENAME* hashArray, KEYTYPE key)	DE_UNUSED_FUNCTION;	\
DE_INLINE void			TYPENAME##_reset			(DE_PTR_TYPE(TYPENAME) hashArray)			DE_UNUSED_FUNCTION;	\
																									\
DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* hashArray)									\
{																									\
	return TYPENAME##Array_getNumElements(hashArray->array);										\
}																									\
																									\
DE_INLINE VALUETYPE* TYPENAME##_find (const TYPENAME* hashArray, KEYTYPE key)						\
{																									\
	int* ndxPtr = TYPENAME##Hash_find(hashArray->hash, key);										\
	if (!ndxPtr)																					\
		return DE_NULL;																				\
	else																							\
	{																								\
		int ndx = *ndxPtr;																			\
		DE_ASSERT(ndx >= 0 && ndx < hashArray->array->numElements);									\
		{																							\
			int pageNdx	= (ndx >> DE_ARRAY_ELEMENTS_PER_PAGE_LOG2);									\
			int subNdx	= ndx & ((1 << DE_ARRAY_ELEMENTS_PER_PAGE_LOG2) - 1);						\
			return &((VALUETYPE*)hashArray->array->pageTable[pageNdx])[subNdx];						\
		}																							\
	}																								\
}																									\
																									\
DE_INLINE void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) hashArray)									\
{																									\
	TYPENAME##Hash_reset(hashArray->hash);															\
	TYPENAME##Array_reset(hashArray->array);														\
}																									\
																									\
struct TYPENAME##Dummy_s { int dummy; }

/*--------------------------------------------------------------------*//*!
 * \brief Implement a template pool hash-array class.
 * \param TYPENAME			Type name of the declared hash.
 * \param KEYTYPE			Type of the key.
 * \param VALUETYPE			Type of the value.
 * \param KEYARRAYTYPE		Type of the key array.
 * \param VALUEARRAYTYPE	Type of the value array.
 * \param HASHFUNC			Function used for hashing the key.
 * \param CMPFUNC			Function used for exact matching of the keys.
 *
 * This macro has implements the hash declared with DE_DECLARE_POOL_HASH.
 * Usually this macro should be used from a .c file, since the macro expands
 * into multiple functions. The TYPENAME, KEYTYPE, and VALUETYPE parameters
 * must match those of the declare macro.
*//*--------------------------------------------------------------------*/
#define DE_IMPLEMENT_POOL_HASH_ARRAY(TYPENAME, KEYTYPE, VALUETYPE, KEYARRAYTYPE, VALUEARRAYTYPE, KEYHASHFUNC, KEYCMPFUNC)			\
																									\
DE_IMPLEMENT_POOL_HASH(TYPENAME##Hash, KEYTYPE, int, KEYHASHFUNC, KEYCMPFUNC);						\
																									\
TYPENAME* TYPENAME##_create (deMemPool* pool)														\
{																									\
	DE_PTR_TYPE(TYPENAME) hashArray = DE_POOL_NEW(pool, TYPENAME);									\
	if (!hashArray) return DE_NULL;																	\
	if ((hashArray->hash = TYPENAME##Hash_create(pool)) == DE_NULL)									\
		return DE_NULL;																				\
	if ((hashArray->array = TYPENAME##Array_create(pool)) == DE_NULL)								\
		return DE_NULL;																				\
	return hashArray;																				\
}																									\
																									\
deBool TYPENAME##_insert (DE_PTR_TYPE(TYPENAME) hashArray, KEYTYPE key, VALUETYPE value)			\
{																									\
	int numElements = TYPENAME##Array_getNumElements(hashArray->array);								\
	DE_ASSERT(TYPENAME##Hash_getNumElements(hashArray->hash) == numElements);						\
	DE_ASSERT(!TYPENAME##Hash_find(hashArray->hash, key));											\
	if (!TYPENAME##Array_setSize(hashArray->array, numElements+1) ||								\
		!TYPENAME##Hash_insert(hashArray->hash, key, numElements))									\
		return DE_FALSE;																			\
	TYPENAME##Array_set(hashArray->array, numElements, value);										\
	return DE_TRUE;																					\
}																									\
																									\
deBool TYPENAME##_copyToArray (const TYPENAME* hashArray, DE_PTR_TYPE(KEYARRAYTYPE) keys, DE_PTR_TYPE(VALUEARRAYTYPE) values)		\
{																									\
	int					numElements	= TYPENAME##Array_getNumElements(hashArray->array);				\
	TYPENAME##Hash*		hash		= hashArray->hash;												\
	TYPENAME##HashIter	iter;																		\
	DE_ASSERT(TYPENAME##Hash_getNumElements(hashArray->hash) == numElements);						\
	if ((keys && !KEYARRAYTYPE##_setSize(keys, numElements)) ||										\
		(values && !VALUEARRAYTYPE##_setSize(values, numElements)))									\
		return DE_FALSE;																			\
	for (TYPENAME##HashIter_init(hash, &iter); TYPENAME##HashIter_hasItem(&iter); TYPENAME##HashIter_next(&iter))	\
	{																								\
		KEYTYPE key	= TYPENAME##HashIter_getKey(&iter);												\
		int		ndx	= TYPENAME##HashIter_getValue(&iter);											\
		if (keys) KEYARRAYTYPE##_set(keys, ndx, key);												\
		if (values) VALUEARRAYTYPE##_set(values, ndx, TYPENAME##Array_get(hashArray->array, ndx));	\
	}																								\
	return DE_TRUE;																					\
}																									\
																									\
struct TYPENAME##Dummy2_s { int dummy; }

#endif /* _DEPOOLHASHARRAY_H */
